/**
 * Copyright (c) 2008, Mu Dynamics.
 *  All rights reserved.
 *  
 *   Redistribution and use in source and binary forms, with or without modification, 
 *   are permitted provided that the following conditions are met:
 *   
 *  - Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice, 
 *     this list of conditions and the following disclaimer in the documentation and/or 
 *     other materials provided with the distribution.
 *  - Neither the name of the "Mu Dynamics" nor the names of its contributors may be used 
 *     to endorse  or promote products derived from this software without specific prior 
 *     written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.mu.jacob.core.model;

import java.util.LinkedHashSet;
import java.util.Set;

import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import com.mu.jacob.core.generator.Decorate;
import com.mu.jacob.core.generator.GeneratorException;
import com.mu.jacob.core.model.decorator.Filter;

/**
 * Default IModel implementation
 * @author Adam Smyczek
 */
public class Model implements IModel {

	/* Compiler environments */
	private ProcessingEnvironment processingEnv;
	private RoundEnvironment roundEnv;
	
	/**
	 * Environment initialization
	 */
	public void initEnvironment(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv) {
		this.processingEnv = processingEnv;
		this.roundEnv = roundEnv;
	}

	/**
	 * @see com.mu.jacob.core.model.IModel#getClasses()
	 */
	@Decorate
	public Set<TypeElement> getClasses() {
		return Filter.<TypeElement>filterElements(roundEnv.getRootElements(), TypeElement.class, ElementKind.CLASS);
	}

	/**
	 * @see com.mu.jacob.core.model.IModel#getInterfaces()
	 */
	@Decorate
	public Set<TypeElement> getInterfaces() {
		return Filter.<TypeElement>filterElements(roundEnv.getRootElements(), TypeElement.class, ElementKind.INTERFACE);
	}

	/**
	 * @see com.mu.jacob.core.model.IModel#getPackages()
	 */
	@Decorate
	public Set<PackageElement> getPackages() {
		Set<PackageElement> packageElements = new LinkedHashSet<PackageElement>();
		for (Element element : roundEnv.getRootElements()) {
			if (element.getEnclosingElement() instanceof PackageElement) {
				packageElements.add(PackageElement.class.cast(element.getEnclosingElement()));
			}
		}
		return packageElements;
	}

	/**
	 * @see com.mu.jacob.core.model.IModel#getRootElements()
	 */
	@Decorate
	public Set<? extends Element> getRootElements() {
		return roundEnv.getRootElements();
	}
	
	/**
	 * @see com.mu.jacob.core.model.IModel#getElementsAnnotatedWith(javax.lang.model.element.TypeElement)
	 */
	@Decorate
	public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
		return roundEnv.getElementsAnnotatedWith(a);
	}
	
	/**
	 * @see com.mu.jacob.core.model.IModel#getElements()
	 */
	@Decorate
	public Elements getElements() {
		return processingEnv.getElementUtils();
	}

	/**
	 * @see com.mu.jacob.core.model.IModel#getTypes()
	 */
	@Decorate
	public Types getTypes() {
		return processingEnv.getTypeUtils();
	}

	/**
	 * @see com.mu.jacob.core.model.IModel#isModelObject(java.lang.Object)
	 */
	public boolean isModelObject(Object object) {
		Element element = null;
		if (object != null) {
			if (object instanceof Element) {
				element = (Element)object;
			} else if (object instanceof TypeMirror) {
				if (object instanceof ArrayType) {
					element = getTypes().asElement(((ArrayType)object).getComponentType());
				} else {
					element = getTypes().asElement((TypeMirror)object);
				}
			} else {
				throw new GeneratorException("Not supported model object type " + object.getClass());
			}
				
			if (element != null) {
				for (Element elm : getRootElements()) {
					if (element.toString().equals(elm.toString())) {
						return true;
					}
				}
			}
		}
		
		return false;
	}
	
	/**
	 * @see com.mu.jacob.core.model.IModel#isObject(java.lang.Object)
	 */
	public boolean isObject(Object object) {
		Element element = null;
		if (object != null) {
			if (object instanceof Element) {
				element = (Element)object;
			} else if (object instanceof TypeMirror) {
				if (object instanceof ArrayType) {
					element = getTypes().asElement(((ArrayType)object).getComponentType());
				} else {
					element = getTypes().asElement((TypeMirror)object);
				}
			} else {
				throw new GeneratorException("Not supported model object type " + object.getClass());
			}
				
			if (element != null) {
				if ("java.lang.Object".equals(((TypeElement)element).getQualifiedName().toString())) {
					return true;
				}
			}
		}
		
		return false;
		
	}
}
